///////////////////////////////////////////////////////////
//  IndexOrganization.cpp
//  Implementation of the Class IndexOrganization
//  Created on:      02-Jun-2007 10:55:32 a.m.
///////////////////////////////////////////////////////////

#include "IndexOrganization.h"
#include "Metadata.h"
#include "FileHandler.h"
#include "BPlusPlus.h"
#include "FileRecord.h"
#include <iostream>
#include <fstream>
#include <string>
#include <cmath>
#include <unistd.h>
#include <fcntl.h>

#ifndef PAGESIZE
	#define PAGESIZE 4096
#endif

#ifndef ZERO
	#define ZERO 0
#endif

#ifndef UNO
	#define UNO 1
#endif

#ifndef PRIMARY_INDEX
	#define PRIMARY_INDEX 0
#endif

#ifndef ERROR
	#define ERROR -1
#endif

#define INDEX_TYPE 0
#define RECORD_TYPE 1
#define METADATA_TYPE 2

#define MAX_MULTIPLE_KEY 256

#define MAX_SIZE_CAD 200

IndexOrganization::IndexOrganization()
{
}


IndexOrganization::~IndexOrganization()
{
}

//--------------------------------------------------------------------------------------------------
//	METODOS PRIVADOS
//--------------------------------------------------------------------------------------------------

bool IndexOrganization::CheckExistence(const char* filename){
	File* file = new File();
	char *dataFilename = new char[MAX_SIZE_CAD];
	bool retorno;

	this->resolveName(filename, RECORD_TYPE, NULL, dataFilename);
	
	retorno = file->exists(dataFilename);

	delete file;
	delete []dataFilename;
	return retorno;
}

bool IndexOrganization::CheckExistence(int handler){
	FileHandler* fileHandler = new FileHandler();
	bool retorno = false;
	char *filename = new char[MAX_SIZE_CAD];

	fileHandler->Open();
	fileHandler->Get(handler, filename);
	retorno = (fileHandler->Get(filename) != ERROR);
	fileHandler->Close();

	delete fileHandler;
	delete []filename;
	return retorno;
}

void IndexOrganization::resolveName(const char* organizationName, unsigned int type, char* field, char* filename)
{
	std::string cadena("");
	// Resuelve los nombres segun la organizacion y el nombre del campo
	if(organizationName != NULL && filename != NULL)
	{
		cadena += organizationName;
		switch(type)
		{
			case INDEX_TYPE: cadena += "INDEX"; break;
			case RECORD_TYPE: cadena += "RECORD"; break;
			case METADATA_TYPE: cadena += "METADATA"; break;
		};
		if(field != NULL)
			cadena += field;
		cadena += ".dat";
		memcpy(filename, (char*)cadena.c_str(), cadena.length());
		filename[cadena.length()] = '\0';
	}	
}

void IndexOrganization::getOrganizationFileName(int handler, char *name)
{
	FileHandler* fileHandler = new FileHandler();
	
	fileHandler->Open();

	fileHandler->Get(handler, name);

	fileHandler->Close();

	delete fileHandler;
}

//--------------------------------------------------------------------------------------------------
//	METODOS PUBLICOS
//--------------------------------------------------------------------------------------------------

int IndexOrganization::I_CREATE(const char* filename, const campo* reg, const campo* clavePrimaria){
	FileRecord* fileRecord;
	FileHandler* fileHandler;
	BPlusPlus* tree;
	Metadata* metadata = Metadata::GetInstance();
	MetadataOrganization *organization;
	int retorno = RES_ERROR;
	char recordName[MAX_SIZE_CAD];
	char metadataName[MAX_SIZE_CAD];
	char indexName[MAX_SIZE_CAD];
	std::string strMetadataName("");
	std::string strIndexName("");
	
	memset(recordName, ZERO, MAX_SIZE_CAD);
	memset(metadataName, ZERO, MAX_SIZE_CAD);
	memset(indexName, ZERO, MAX_SIZE_CAD);
	

	if(!this->CheckExistence(filename))
	{
		tree = new BPlusPlus();
		fileRecord = new FileRecord();
		fileHandler = new FileHandler();
		organization = new MetadataOrganization();
		
		fileHandler->Create();
		fileHandler->Open();
		retorno = fileHandler->Add((const char*)filename);
		fileHandler->Close();

		organization->setHandler(retorno);
		
		// Creo el archivo administrativo de indices y agrego el indice primario.
		this->resolveName(filename, METADATA_TYPE, "", metadataName);
		strMetadataName += metadataName;
		organization->setName(strMetadataName);

		for(int i = 0; reg[i].nombre != ZERO; i++)
		{
			organization->addField((campo*)&(reg[i]));
		}
		organization->addIndex((campo*)clavePrimaria, strIndexName);
		
		this->resolveName(filename, RECORD_TYPE, NULL, recordName);
		if(fileRecord->Create((const char*)recordName) > ZERO)
			retorno = RES_NULL;
		
		this->resolveName(filename, INDEX_TYPE, (char*)strIndexName.c_str(), indexName);
		tree->create(indexName);
		tree->close();
		metadata->add(organization);
		
		delete tree;
		delete fileRecord;
		delete fileHandler;
		retorno = RES_OK;
	}
	else
		retorno = RES_EXISTE;
	return retorno;
}


int IndexOrganization::I_OPEN(const char* filename, int modo){
	int retorno = RES_NULL;
	FileHandler* handlers;
	Metadata* metadata = Metadata::GetInstance();
	MetadataOrganization *organization;
	char metadataName[MAX_SIZE_CAD];
	std::string strMetadataName("");
	
	if(this->CheckExistence(filename))
	{
		handlers = new FileHandler();
		handlers->Open();
		retorno = handlers->Get(filename);
		handlers->Close();
		
		if(!metadata->is(retorno))
		{
			organization = new MetadataOrganization();
			organization->setHandler(retorno);
			
			this->resolveName(filename, METADATA_TYPE, "", metadataName);
			strMetadataName += metadataName;
			organization->setName(strMetadataName);
			
			metadata->load(organization);
		}
		
		delete handlers;
	}
	else
		retorno = RES_NO_EXISTE;

	return retorno;
}


int IndexOrganization::I_CLOSE(int handler){
	int retorno = RES_OK;
	Metadata* metadata = Metadata::GetInstance();
	MetadataOrganization *organization;
		
	if(!this->CheckExistence(handler))
	{
		retorno = RES_ERROR;
	}
	else
	{		
		organization = metadata->get(handler);
		metadata->save(organization);
	}


	return retorno;
}

int IndexOrganization::I_DESTROY(const char* filename){
	int retorno = RES_OK;
	FileHandler* fileHandler = NULL;
	FileRecord* fileRecord = NULL;
	Metadata* metadata = Metadata::GetInstance();
	MetadataOrganization *organization;
	int handler = ZERO;

	std::string strIndexName("");
	
	char indexFieldname[MAX_SIZE_CAD];
	char recordName[MAX_SIZE_CAD];
	char metadataName[MAX_SIZE_CAD];

	memset(indexFieldname, ZERO, MAX_SIZE_CAD);
	memset(recordName, ZERO, MAX_SIZE_CAD);
	memset(metadataName, ZERO, MAX_SIZE_CAD);
		
	if(this->CheckExistence(filename))
	{
		fileHandler = new FileHandler();
		
		fileHandler->Create();
		fileHandler->Open();
		handler = fileHandler->Get((const char*)filename);		
		organization = metadata->get(handler);

		fileHandler->Remove(handler);
		fileHandler->Close();
		delete fileHandler;

		organization->startIndexIterator();
		
		while(organization->hasNextIndex())
		{
			organization->getIndexName(organization->nextIndex(), strIndexName);
			this->resolveName(filename, INDEX_TYPE, (char*)strIndexName.c_str(), indexFieldname);
			remove(indexFieldname);
		}
		
		
		fileRecord = new FileRecord();
		this->resolveName(filename, RECORD_TYPE, NULL, recordName);
		fileRecord->Destroy((const char*)recordName);
		delete fileRecord;

		this->resolveName(filename, METADATA_TYPE, NULL, metadataName);
		remove(metadataName);
	}
	return retorno;
}


int IndexOrganization::I_ADD_INDEX(int handler, const campo* fields){
	int retorno = RES_ERROR;
	BPlusPlus* tree=NULL;
	FileRecord* fileRecord=NULL;
	Metadata* metadata = Metadata::GetInstance();
	MetadataOrganization *organization;
	
	std::string strIndexName("");
	int idIndex = ZERO;

	char filename[MAX_SIZE_CAD];
	char recordName[MAX_SIZE_CAD];
	char indexName[MAX_SIZE_CAD];

	memset(filename, ZERO, MAX_SIZE_CAD);
	memset(recordName, ZERO, MAX_SIZE_CAD);
	memset(indexName, ZERO, MAX_SIZE_CAD);

	this->getOrganizationFileName(handler, filename);

	if(this->CheckExistence(filename))
	{		
		organization = metadata->get(handler);

		if(!organization->isIndex((campo*)fields, idIndex))
		{
			idIndex = organization->addIndex((campo*)fields, strIndexName);

			this->resolveName(filename, INDEX_TYPE, (char*)strIndexName.c_str(), indexName);
			
			tree = new BPlusPlus();
			std::string name(indexName);
			tree->create(name);

			fileRecord = new FileRecord();
			this->resolveName(filename, RECORD_TYPE, NULL, recordName);
			fileRecord->Open(recordName);
			
			long int offset = ZERO;
			
			while(fileRecord->HasNext() && offset != RES_NULL)
			{
				
				unsigned int size  = ZERO;
				unsigned int sizeKey = ZERO;
				char record[PAGESIZE];
				char key[PAGESIZE];
 
				memset(key, 0, PAGESIZE);
				memset(record, 0, PAGESIZE);
				
				fileRecord->Next(offset, record, size);

				if(offset != RES_NULL)
				{
					organization->GetKey(record, idIndex, key, sizeKey);
					
					if(strcmp(key, "") != ZERO)
						tree->add(key, sizeKey, offset);
				}
			}
			
			fileRecord->Close();
			tree->close();
			delete fileRecord;
			delete tree;

			retorno = idIndex;
		}
		else
			retorno = RES_EXISTE;
	}

	return retorno;
}


int IndexOrganization::I_DROP_INDEX(int handler, int indexId){
	int retorno = RES_OK;
	Metadata* metadata = Metadata::GetInstance();
	MetadataOrganization *organization;
	std::string strIndexName("");
	char filename[MAX_SIZE_CAD];
	char indexname[MAX_SIZE_CAD];
	
	memset(filename, ZERO, MAX_SIZE_CAD);
	memset(indexname, ZERO, MAX_SIZE_CAD);
		
	this->getOrganizationFileName(handler, filename);
	
	if(this->CheckExistence(filename))
	{
		
		organization = metadata->get(handler);
		
		if(organization->isIndexByID(indexId))
		{
			organization->dropIndex(indexId, strIndexName);
	
			this->resolveName(filename, INDEX_TYPE, (char*)strIndexName.c_str(), indexname);
	
			remove((char*)indexname);
		}
		else
			retorno = RES_NO_EXISTE_INDICE;
	}
	return retorno;
}


int IndexOrganization::I_IS_INDEX(int handler, campo* clave){
	int retorno = RES_ERROR;

	Metadata* metadata = Metadata::GetInstance();
	MetadataOrganization *organization;

	if(this->CheckExistence(handler))
	{
		organization = metadata->get(handler);

		if(!organization->isIndex(clave, retorno))
			retorno = RES_NO_EXISTE_INDICE;
	}
	return retorno;
}


int IndexOrganization::I_START(int handler, int indexId, char* operador, const void* valorReferencia){
	int retorno = RES_ERROR;
	unsigned int size = ZERO;
	BPlusPlus* tree;

	Metadata* metadata = Metadata::GetInstance();
	MetadataOrganization *organization;
	std::string strIndexName("");

	char filename[MAX_SIZE_CAD];
	char indexname[MAX_SIZE_CAD];
	char clave[MAX_SIZE_CAD];
	
	memset(indexname, ZERO, MAX_SIZE_CAD);
	memset(filename, ZERO, MAX_SIZE_CAD);
	memset(clave, ZERO, MAX_SIZE_CAD);

	this->getOrganizationFileName(handler, filename);

	if(this->CheckExistence(filename))
	{
		organization = metadata->get(handler);

		if(organization->isIndexByID(indexId))
		{
			organization->getIndexName(indexId, strIndexName);

			tree = new BPlusPlus();

			this->resolveName(filename, INDEX_TYPE, (char*)strIndexName.c_str(), indexname);

			std::string name(indexname);

			tree->open(name);

			organization->GetKey((void*)valorReferencia, indexId, (char*)clave, size);

			tree->start(operador, clave, size);

			retorno = RES_OK;
			tree->close();
			delete tree;
		}
		else
			retorno = RES_NO_EXISTE_INDICE;
	}
	return retorno;
}

int IndexOrganization::I_READ_NEXT(int idHandler, int idIndex, void* reg){
	int retorno = RES_ERROR;
	BPlusPlus* tree = NULL;
	FileRecord* fileRecord = NULL;
	Metadata* metadata = Metadata::GetInstance();
	MetadataOrganization *organization;
	std::string strIndexName("");
	Key* clave = new Key();
	unsigned int size = ZERO;
	long int offset = ZERO;

	char filename[MAX_SIZE_CAD];
	char indexname[MAX_SIZE_CAD];
	char recordname[MAX_SIZE_CAD];

	memset(filename, ZERO, MAX_SIZE_CAD);
	memset(indexname, ZERO, MAX_SIZE_CAD);
	memset(recordname, ZERO, MAX_SIZE_CAD);

	if(reg != NULL)
	{
		this->getOrganizationFileName(idHandler, filename);

		if(this->CheckExistence(filename))
		{
			organization = metadata->get(idHandler);

			if(organization->isIndexByID(idIndex))
			{
				organization->getIndexName(idIndex, strIndexName);
				
				this->resolveName(filename, INDEX_TYPE, (char*)strIndexName.c_str(), indexname);

				fileRecord = new FileRecord();
				this->resolveName(filename, RECORD_TYPE, NULL, recordname);
				fileRecord->Open((const char*)recordname);
		
				std::string name(indexname);
				tree = new BPlusPlus();
				tree->open(name);

				if(tree->hasNext())
				{
					tree->next(clave);
					offset = clave->getOffset();
					if(offset > ZERO)
					{
						fileRecord->Get(offset, (char*)reg, size);
						
						retorno = RES_OK;
					}
					else
						retorno = RES_EOF;
				}
				else
					retorno = RES_EOF;
				fileRecord->Close();
				tree->close();
				delete tree;
				delete fileRecord;	
			}
		}
		
	}
	delete clave;
	return retorno;
}


int IndexOrganization::I_READ(int handler, void* reg){
	int retorno = RES_ERROR;
	unsigned int size = ZERO;
	Metadata* metadata = Metadata::GetInstance();
	MetadataOrganization *organization;
	std::string strIndexName("");
	BPlusPlus* tree = NULL;
	FileRecord* fileRecord = NULL;
	long int offset = ZERO;
	char *key = NULL;
	char filename[MAX_SIZE_CAD];
	char indexname[MAX_SIZE_CAD];
	char recordname[MAX_SIZE_CAD];	

	memset(filename, ZERO, MAX_SIZE_CAD);
	memset(indexname, ZERO, MAX_SIZE_CAD);
	memset(recordname, ZERO, MAX_SIZE_CAD);

	this->getOrganizationFileName(handler, filename);

	if(this->CheckExistence(filename))
	{
		organization = metadata->get(handler);

		organization->getIndexName(PRIMARY_INDEX, strIndexName);

		this->resolveName(filename, INDEX_TYPE, (char*)strIndexName.c_str(), indexname);
		std::string name(indexname);
		tree = new BPlusPlus();
		tree->open(name);

		key = new char[PAGESIZE];

		organization->GetKey((void*)reg, PRIMARY_INDEX, key, size);

		//std::cout << "KEY: " << (int)(*((int*)key)) << std::endl;

		offset = tree->get(key, size);

		if(offset > ZERO)
		{
			fileRecord = new FileRecord();
			this->resolveName(filename, RECORD_TYPE, NULL, recordname);
			fileRecord->Open((const char*)recordname);
			fileRecord->Get(offset, (char*)reg, size);
			fileRecord->Close();
			delete fileRecord;
			retorno = RES_OK;
			
		}
		else
			retorno = RES_ERROR;

		tree->close();
		delete tree;
		
		delete []key;
	}
	return retorno;
}

bool IndexOrganization::emptyKey(char *key, unsigned int size)
{
	bool retorno = false;
	
	if(key != NULL)
	{
		for(unsigned int i = ZERO; i < size && !retorno; i++)
		{
			if(key[ZERO] != '\0')
				retorno = true;
		}
	}
	return !retorno;
}

int IndexOrganization::I_WRITE(int handler, const void* reg){
	int retorno = RES_ERROR;
	unsigned int size = ZERO;
	long int offset = ZERO;
	BPlusPlus* tree = NULL;
	FileRecord* fileRecord = NULL;
	Metadata* metadata = Metadata::GetInstance();
	MetadataOrganization *organization;
	std::string strIndexName("");
	std::string name("");
	char filename[MAX_SIZE_CAD];
	char indexname[MAX_SIZE_CAD];
	char key[PAGESIZE];
	char recordname[MAX_SIZE_CAD];	
	
	memset(filename, ZERO, MAX_SIZE_CAD);
	memset(indexname, ZERO, MAX_SIZE_CAD);
	memset(key, ZERO, PAGESIZE);
	memset(recordname, ZERO, MAX_SIZE_CAD);
	
	this->getOrganizationFileName(handler, filename);
	
	if(this->CheckExistence(filename))
	{
		organization = metadata->get(handler);
		organization->getIndexName(PRIMARY_INDEX, strIndexName);

		this->resolveName(filename, INDEX_TYPE, (char*)strIndexName.c_str(), indexname);
		name += indexname;
		strIndexName.clear();
		tree = new BPlusPlus();
		tree->open(name);
		organization->GetKey((void*)reg, PRIMARY_INDEX, key, size);
		offset = tree->get(key, size);
		tree->close();
		delete tree;


		if(offset > ZERO)
		{ // El registro existe
			retorno = RES_EXISTE;
		}
		else
		{
			if(offset == B_NO_EXISTE && !this->emptyKey(key, size))
			{
				fileRecord = new FileRecord();
				this->resolveName(filename, RECORD_TYPE, NULL, recordname);
				fileRecord->Open((const char*)recordname);
	
				// El registro no existe, por lo tanto lo agregos
				unsigned int sizeRecord = ((unsigned int*)reg)[ZERO];
				if(FR_OK == fileRecord->Add((char*)reg, sizeRecord, offset))
				{
					organization->startIndexIterator();
					while(organization->hasNextIndex())
					{
						Index index;
						
						organization->getIndex(organization->nextIndex(), &index);
	// 					campo campos[MAX_MULTIPLE_KEY];
	// 					unsigned int pos = ZERO;
	// 					organization.startFieldIndexIterator(index.getID());
	// 					while(organization.hasNextFieldIndex())
	// 					{
	// 						Field field;
	// 						int id = ZERO;
	// 						campos[pos]
	// 						organization.getField(organization.nextFieldIndex(), &field);
	// 						field.get(id, (campo*)&(campos[pos]));
	// 						pos++;
	// 					}
	// 					campos[pos].nombre = ZERO;
	// 					campos[pos].tipo = ZERO;
	// 					campos[pos].longitud = ZERO;
	
						organization->getIndexName(index.getID(), strIndexName);
	
						this->resolveName(filename, INDEX_TYPE, (char*)strIndexName.c_str(), indexname);
						name = indexname;
						strIndexName.clear();
						tree = new BPlusPlus();
						tree->open(name);
						organization->GetKey((void*)reg, index.getID(), key, size);
						if(!this->emptyKey(key, size))
							tree->add(key, size, offset);
						else
							retorno = RES_ERROR;
						tree->close();
						delete tree;
					}
		
					retorno = RES_OK;
				}
				else
					retorno = RES_ERROR;
	
				fileRecord->Close();
				delete fileRecord;
			}
			else
				retorno = RES_ERROR;
		}
	}
	strIndexName.clear();
	name.clear();
	return retorno;
}


int IndexOrganization::I_UPDATE(int handler, const void* reg){
	int retorno = RES_ERROR;	
	
	return retorno;
}


int IndexOrganization::I_DELETE(int handler, const void* reg){
	int retorno = RES_ERROR;
	
	return retorno;
}

int IndexOrganization::I_MAX(int handler, int indexId, void* reg)
{
	int retorno = RES_ERROR;
	unsigned int size = ZERO;
	long int offset = ZERO;
	BPlusPlus* tree = NULL;
	FileRecord* fileRecord = NULL;
	Metadata* metadata = Metadata::GetInstance();
	MetadataOrganization *organization;
	std::string strIndexName("");
	std::string name("");
	char filename[MAX_SIZE_CAD];
	char indexname[MAX_SIZE_CAD];
	char key[PAGESIZE];
	char recordname[MAX_SIZE_CAD];	
	
	memset(filename, ZERO, MAX_SIZE_CAD);
	memset(indexname, ZERO, MAX_SIZE_CAD);
	memset(key, ZERO, PAGESIZE);
	memset(recordname, ZERO, MAX_SIZE_CAD);

	this->getOrganizationFileName(handler, filename);

	if(this->CheckExistence(handler))
	{
		organization = metadata->get(handler);

		if(organization->isIndexByID(indexId))
		{
			organization->getIndexName(indexId, strIndexName);
			this->resolveName(filename, INDEX_TYPE, (char*)strIndexName.c_str(), indexname);
			name += indexname;
			tree = new BPlusPlus();
			tree->open(name);
			offset = tree->Max();
			tree->close();
			delete tree;

			if(offset > ZERO)
			{
				fileRecord = new FileRecord();
				this->resolveName(filename, RECORD_TYPE, NULL, recordname);
				fileRecord->Open((const char*)recordname);
				fileRecord->Get(offset, (char*)reg, size);
				retorno = RES_OK;
				fileRecord->Close();
				delete fileRecord;
			}
		}
		else
			retorno = RES_NO_EXISTE_INDICE;
	}
	return retorno;
}

int IndexOrganization::I_MIN(int handler, int indexId, void* reg)
{
	int retorno = RES_ERROR;
	unsigned int size = ZERO;
	long int offset = ZERO;
	BPlusPlus* tree = NULL;
	FileRecord* fileRecord = NULL;
	Metadata* metadata = Metadata::GetInstance();
	MetadataOrganization *organization;
	std::string strIndexName("");
	std::string name("");
	char filename[MAX_SIZE_CAD];
	char indexname[MAX_SIZE_CAD];
	char key[PAGESIZE];
	char recordname[MAX_SIZE_CAD];	
	
	memset(filename, ZERO, MAX_SIZE_CAD);
	memset(indexname, ZERO, MAX_SIZE_CAD);
	memset(key, ZERO, PAGESIZE);
	memset(recordname, ZERO, MAX_SIZE_CAD);

	this->getOrganizationFileName(handler, filename);

	if(this->CheckExistence(handler))
	{
		organization = metadata->get(handler);

		if(organization->isIndexByID(indexId))
		{
			organization->getIndexName(indexId, strIndexName);
			this->resolveName(filename, INDEX_TYPE, (char*)strIndexName.c_str(), indexname);
			name += indexname;
			tree = new BPlusPlus();
			tree->open(name);
			offset = tree->Min();
			tree->close();
			delete tree;

			if(offset > ZERO)
			{
				fileRecord = new FileRecord();
				this->resolveName(filename, RECORD_TYPE, NULL, recordname);
				fileRecord->Open((const char*)recordname);
				fileRecord->Get(offset, (char*)reg, size);
				retorno = RES_OK;
				fileRecord->Close();
				delete fileRecord;
			}
		}
		else
			retorno = RES_NO_EXISTE_INDICE;
	}
	return retorno;
}

